﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.Serialization;
using CI.Interfaces;
using CI.Mesopotamia.Neural;

namespace CI.Organism
{
    /// <summary>Emulate a very simple life form, just  motor drive and left and right bumper</summary>
    [DataContract]
    public class iRobot : Cell, IPhysical
    {
        /// <summary>Emulate a very simple life form, just  motor drive and left and right bumper</summary>
        public iRobot(IOrganism organism, Guid guid)
            : base(organism, guid)
        {
            // add the bumpers and wheels with direction
            #region Create IO Manifest

            _ioManifest.Add(new SynapseIO(Globals.IROBOTLEFTFRONTBUMPER, "Left front bumper", "", new Guid("34EABAC6-58D5-4F29-BDE9-7067F93D588F")));
            _ioManifest.Add(new SynapseIO(Globals.IROBOTRIGHTFRONTBUMPER, "Right front bumper", "", new Guid("40ED637D-6459-44E5-AB9F-00A6843A2D76")));
            _ioManifest.Add(new SynapseIO(Globals.IROBOTFORWARD, "Forward", "", new Guid("D230C47D-E666-441A-A26F-CE862F10BFA4")));
            _ioManifest.Add(new SynapseIO(Globals.IROBOTTURNRIGHT, "Turn right", "", new Guid("587FDF91-4C7B-4C86-AEAC-B61D0096175F")));
            _ioManifest.Add(new SynapseIO(Globals.IROBOTTURNLEFT, "Turn left", "", new Guid("C33460E0-3FE7-4BFD-97C3-E64F7B6BBAAF")));
            _ioManifest.Add(new SynapseIO(Globals.ENERGY, "Energy", "", new Guid("5D94517C-F8A7-4E7F-AC04-C11A46109AF1")));
            _ioManifest.Add(new SynapseIO(Globals.ENERGYCHANGE, "Energy Change", "", new Guid("3F308C1A-E614-43DA-9668-04B591605E50")));
            _ioManifest.Add(new VisualSynapseIO(Globals.CYCLOPS, "Cyclops", "A grey scale single pixel vision system", new Guid("{6A1EA235-70F6-4BEE-9AAD-8F18DD389A97}"), System.Guid.NewGuid(), ChildSynapseIOParentType.SimpleVisionSystem, 0, 0, SVSFormatType.GreyScale));


            #endregion
        }

        /// <summary>
        /// Take this parent cell and create a new cell to be put in the new child Organism
        /// Mutate the current cell and return a new one, deep class level mutation
        /// </summary>
        /// <param name="mutationRate">mutation rate to be applied</param>
        /// <param name="childOrganism">child child Organism it will be going into</param>
        /// <returns>new cell for child</returns>
        public override ICell Mutate(double mutationRate, IOrganism childOrganism)
        {
            // create a new cell with the same guid
            var childcell = new iRobot(childOrganism, Guid) { Guid = Guid };

            return childcell;
        }

        /// <summary>IO manifest, used by neural network when mutating</summary>
        public ReadOnlyCollection<SynapseIO> IOManifest
        {
            // Return a wrapped copy of the results.
            get { return new ReadOnlyCollection<SynapseIO>(_ioManifest); }
        }
        [DataMember]
        private SynchronizedCollection<SynapseIO> _ioManifest = new SynchronizedCollection<SynapseIO>();

        /// <summary>
        /// To String override
        /// </summary>
        /// <returns></returns>
        public override string ToString(string value)
        {
            return ToString();
        }

        /// <summary>
        /// basic tostring
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "iRobot";
        }


        ///<summary>
        /// Bind the organisms SynapseIO to IO
        ///</summary>
        ///<param name="organism"></param>
        ///<param name="leftBumper"></param>
        ///<param name="rightBumper"></param>
        ///<param name="cameraFeed"></param>
        ///<param name="turnLeft"></param>
        ///<param name="turnRight"></param>
        ///<param name="forward"></param>
        public static void BindSynapseIO(IOrganism organism, ref SynapseIODelegate leftBumper, ref SynapseIODelegate rightBumper, ref SynapseIODelegate cameraFeed, ref  Action<Int64> turnLeft, ref Action<Int64> turnRight, ref Action<Int64> forward)
        {
            foreach (var synapseIO in organism.GetIOManifest())
            {
                SimEnv.Instance.TraceLogger.LogDebug(string.Format("Binding {0} on {1} syanpse", synapseIO.BindingName, synapseIO.Guid));

                switch (synapseIO.BindingName)
                {
                    case Globals.ENERGY:
                        organism.EnergySetEvent += synapseIO.FireInput;
                        break;

                    case Globals.ENERGYCHANGE:
                        organism.EnergyChangeEvent += synapseIO.FireInput;
                        break;

                    case Globals.IROBOTLEFTFRONTBUMPER:
                        leftBumper += synapseIO.FireInput;
                        break;

                    case Globals.IROBOTRIGHTFRONTBUMPER:
                        rightBumper += synapseIO.FireInput;
                        break;

                    case Globals.CYCLOPS:
                        cameraFeed += synapseIO.FireInput;
                        break;

                    case Globals.IROBOTFORWARD:
                        synapseIO.ActivationEvent += forward;
                        break;

                    case Globals.IROBOTTURNLEFT:
                        synapseIO.ActivationEvent += turnLeft;
                        break;

                    case Globals.IROBOTTURNRIGHT:
                        synapseIO.ActivationEvent += turnRight;
                        break;

                    default:
                        throw new NotImplementedException("Unknown binding");
                }

            }

        }

        ///<summary>
        /// Unind the organisms SynapseIO to IO
        ///</summary>
        ///<param name="organism"></param>
        ///<param name="leftBumper"></param>
        ///<param name="rightBumper"></param>
        ///<param name="cameraFeed"></param>
        ///<param name="turnLeft"></param>
        ///<param name="turnRight"></param>
        ///<param name="forward"></param>
        public static void UnbindSynapseIO(IOrganism organism, ref SynapseIODelegate leftBumper, ref SynapseIODelegate rightBumper, ref SynapseIODelegate cameraFeed, ref  Action<Int64> turnLeft, ref Action<Int64> turnRight, ref Action<Int64> forward)
        {
            foreach (var synapseIO in organism.GetIOManifest())
            {
                SimEnv.Instance.TraceLogger.LogDebug(string.Format("Unbinding {0} on {1} syanpse", synapseIO.BindingName, synapseIO.Guid));

                switch (synapseIO.BindingName)
                {
                    case Globals.ENERGY:
                        organism.EnergySetEvent -= synapseIO.FireInput;
                        break;

                    case Globals.ENERGYCHANGE:
                        organism.EnergyChangeEvent -= synapseIO.FireInput;
                        break;

                    case Globals.IROBOTLEFTFRONTBUMPER:
                        leftBumper -= synapseIO.FireInput;
                        break;

                    case Globals.IROBOTRIGHTFRONTBUMPER:
                        rightBumper -= synapseIO.FireInput;
                        break;


                    case Globals.CYCLOPS:
                        cameraFeed -= synapseIO.FireInput;
                        break;

                    case Globals.IROBOTFORWARD:
                        synapseIO.ActivationEvent -= forward;
                        break;

                    case Globals.IROBOTTURNLEFT:
                        synapseIO.ActivationEvent -= turnLeft;
                        break;

                    case Globals.IROBOTTURNRIGHT:
                        synapseIO.ActivationEvent -= turnRight;
                        break;

                    default:
                        throw new NotImplementedException("Unknown binding");
                }

            }

        }

        /// <summary>
        /// Get the synapseIO with the corresponding binding name
        /// </summary>
        /// <param name="bindingName"></param>
        /// <returns></returns>
        public SynapseIO GetSynapseIO(string bindingName)
        {
            return IOManifest.FirstOrDefault(s => s.BindingName == bindingName);
        }

        /// <summary>
        /// Get the synapseIO with the corresponding Guid
        /// </summary>
        /// <param name="guid"></param>
        /// <returns></returns>
        public SynapseIO GetSynapseIO(Guid guid)
        {
            return IOManifest.FirstOrDefault(s => s.Guid == guid);
        }
    }
}